#include "DataConnection.h"
#include "SocketException.h"

DataConnection::DataConnection(): server(0), client(0), filesource(0), filesink(0), transferDirection(NONE)
{
}


DataConnection::~DataConnection()
{
	Clear();
}

void DataConnection::SetFileSink(const std::string& path)
{
	filesink = fopen(path.c_str(), "wb");

	if (filesink == 0)
	{
		if (observer)
		{
			observer->OnDataSinkError();
		}

		Clear();
	}
}

void DataConnection::SetFileSource(const std::string& path)
{
	filesource = fopen(path.c_str(), "rb");

	if (filesource == 0)
	{
		if (observer)
		{
			observer->OnDataSourceError();
		}

		Clear();
	}
}

void DataConnection::SetActive(DataConnection::TransferDirection direction, const std::string& server, unsigned short port)
{
	if (direction == NONE)
		throw "DataConnection: Invalid transfer direction";

	SetTransferDirection(direction);

	client = new ClientSocket();

	try
	{
		client->Connect(server, port);

		if (observer)
			observer->OnDataStarted();
	} 
	catch(SocketException &e)
	{
		fprintf(stderr, "Error: %s\n", e.what());

		if (observer)
		{
			observer->OnDataConnectError();
		}

		Clear();
	}
}

void DataConnection::Clear()
{
	if (server)
	{
		delete server;
		server = 0;
	}

	if (client)
	{
		delete client;
		client = 0;
	}

	if (filesink)
	{
		fclose(filesink);
		filesink = 0;
	}

	if (filesource)
	{
		fclose(filesource);
		filesource = 0;
	}

	dataupload.Clear();
	datadownload.Clear();
}

void DataConnection::RegisterObserver(DataConnectionObserver* newObserver)
{
	observer = newObserver;
}

void DataConnection::RemoveObserver()
{
	observer = 0;
}

bool DataConnection::IsDownloadConnection() const
{
	return transferDirection == DOWNLOAD;
}

bool DataConnection::IsUploadConnection() const
{
	return transferDirection == UPLOAD;
}

bool DataConnection::IsUploadQueueEmpty() const
{
	if (filesource == 0 && dataupload.Size() == 0)
		return true;
	else
		return false;
}

void DataConnection::SetTransferDirection(DataConnection::TransferDirection direction)
{
	transferDirection = direction;
}


void DataConnection::Upload(const void* data, unsigned int size)
{
	if (transferDirection == UPLOAD)
		dataupload.Write(data, size);
}

bool DataConnection::IsDead() const
{
	bool result = false;


	if (client == 0 && server == 0)
	{
		result = true;
	}
	else if (Socket().IsClosed())
	{
		result = true;
	}

	return result;
}

Socket& DataConnection::Socket() const
{
	if (client)
		return *client;
	if (server)
		return *server;

	throw "DataConnection: Tried to get socket from non-active data connection";
}


void DataConnection::UploadUpdate()
{
	unsigned char buffer[8*1024];
	unsigned int uploadSize = 0;

	if (IsDead())
		return;


	if (filesource)
	{
		uploadSize = fread(buffer, sizeof(unsigned char), sizeof(buffer), filesource);

		if (!uploadSize)
		{
			fclose(filesource);
			filesource = 0;
		}
	}
	else
	{
		uploadSize = dataupload.Read(buffer, sizeof(buffer));
	}

	if (uploadSize)
	{
		Socket().Send(buffer, uploadSize);
	}

	// If we have no more data to send
	if (IsUploadQueueEmpty())
	{
		if (observer)
			observer->OnDataEnded();
		Clear();
	}
}

void DataConnection::DownloadUpdate()
{
	unsigned char buffer[8*1024];

	if (IsDead())
		return;

	unsigned int downloadSize = Socket().Receive(buffer, sizeof(buffer));

	if (Socket().IsClosed())
	{
		if (observer)
			observer->OnDataEnded();
		Clear();
	}
	else
	{
		if (filesink)
		{
			fwrite(buffer, sizeof(unsigned char), downloadSize, filesink);
		}
		else
		{
			datadownload.Write(buffer, downloadSize);
		}
	}
}

